package com.xiaojimao.Service.Impl;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.xaiojimao.gr_common.CommonEnum.BizCommonEnum;
import com.xaiojimao.gr_common.dto.PageResult;
import com.xaiojimao.gr_common.dto.Result;
import com.xaiojimao.gr_common.exception.CommonException;
import com.xiaojimao.Mapper.ArticleMapper;
import com.xiaojimao.Pojo.Article;
import com.xiaojimao.Pojo.ArticleImages;
import com.xiaojimao.Pojo.ArticleReadnum;
import com.xiaojimao.Service.ArticleImageService;
import com.xiaojimao.Service.ArticleReadnumService;
import com.xiaojimao.Service.ArticleService;
import com.xiaojimao.Service.SensitiveWordsService;
import com.xiaojimao.Utils.ArticleConstant;
import com.xiaojimao.Utils.ThreadLocalUtil;
import com.xiaojimao.Vo.ArticleVo;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.xaiojimao.gr_common.CommonEnum.BizCommonEnum.USER_NOT_EXITS_ERROR;

@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    @Autowired
    SensitiveWordsService sensitiveWordsService;

    @Autowired
    ArticleImageService articleImageService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    ArticleReadnumService articleReadnumService;

    @Override
    public Result getByArticleId(Long id) {
        Article byId = getById(id);
        if (byId == null) {
            return Result.fail(BizCommonEnum.ARTICLE_NOT_EXITS_ERROR);
        } else {
            if (byId.getStatus() == 1) {
                return Result.success(byId);
            } else {
                return Result.fail(BizCommonEnum.ARTICLE_STATUS_ERROR);
            }
        }
    }

    @Override
    @Transactional
    public Result createArticle(ArticleVo article) {
        // 文章作者健壮性判断
        if (article.getAuthorId() == null) {
            if (ThreadLocalUtil.getUserId() == null) {
                throw new CommonException(USER_NOT_EXITS_ERROR.getCode(), USER_NOT_EXITS_ERROR.getMsg());
            } else {
                // 设置当前登录用户为作者
                article.setAuthorId(ThreadLocalUtil.getUserId());
            }
        }
        if (StringUtils.isEmpty(article.getCover())) {
            throw CommonException.DefultCommonException(BizCommonEnum.ARTICLE_COVER_NOT_EXITS_ERROR);
        }
        // 初始化文章状态
        // 文章没有封面图片
        if (article.getCover() == null) {
            // 设置第一张图片为封面图片
            article.setCover(article.getImageList().get(0));
        }
        article.setStatus(0);
        article.setArticleVerified(3);
        article.setUpdateTime(LocalDateTime.now());
        article.setCreateTime(LocalDateTime.now());
        // 保存文章到数据库
        save(article);
        // 保存图片路径到数据库
        List<ArticleImages> images = article.getImageList().stream().map(item -> {
            ArticleImages articleImages = new ArticleImages();
            articleImages.setUrl(item);
            articleImages.setArticleId(article.getId());
            return articleImages;
        }).collect(Collectors.toList());
        articleImageService.saveBatch(images);

        // 保存之后，发送消息审核该文章
        rabbitTemplate.convertAndSend("article.exchange", "review", JSON.toJSONString(article));
        return Result.success();
    }

    @Override
    public Result getArticleList(Integer pageNum, Integer pageSize) {
        if (pageNum == null) {
            pageNum = 1;
        }
        if (pageSize == null) {
            pageSize = 10;
        }
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        // 对创建时间进行排序
        queryWrapper.orderByDesc(Article::getCreateTime);
        // 状态为显示的
        queryWrapper.eq(Article::getStatus, 1);
        // 构建page对象
        IPage<Article> page = new Page<>(pageNum, pageSize);
        // 查询数据
        page = page(page, queryWrapper);
        // 构建page 返回对象 填充数据
        PageResult result = new PageResult(page.getTotal(), page.getCurrent(), page.getSize());
        result.setCode(200);
        result.setMsg("成功");
        // 获得数据集
        List<Article> records = page.getRecords();
        // 获得所有的id集
        List<Long> ids = records.stream().map(Article::getId).collect(Collectors.toList());
        // 查询阅读量数据库
        List<ArticleReadnum> articleReadnums = articleReadnumService.listByIds(ids);
        List<Long> SQLReadNums = articleReadnums.stream().map(ArticleReadnum::getArticleId).collect(Collectors.toList());
        List<ArticleVo> voList = ids.stream().map(item -> {
            // 获得对应的数据集
            List<Article> collect = records.stream().filter(recordsItem -> item.equals(recordsItem.getId())).collect(Collectors.toList());
            // 数据拷贝
            ArticleVo articleVo = new ArticleVo();
            BeanUtils.copyProperties(collect.get(0), articleVo);

            if (!SQLReadNums.contains(item)) {
                // 数据库中不存在阅读历史
                Set<Object> readNumKeys = redisTemplate.opsForHash().keys(ArticleConstant.KEY_ARTICLE_READ_NUM + item);
                if (readNumKeys.isEmpty()) {
                   // redis中也不存在阅读记录
                    articleVo.setRedNum(0);
                } else {
                    // 今日阅读总量
                    AtomicInteger currentNum = new AtomicInteger(0);
                    // redis中存在阅读记录
                    readNumKeys.forEach(key -> {
                        // 获得每一个用户阅读了多少次
                        Integer num = (Integer) redisTemplate.opsForHash().get(ArticleConstant.KEY_ARTICLE_READ_NUM + item, key);
                        // 进行累加
                        currentNum.set(currentNum.get() + num);
                    });
                    articleVo.setRedNum(currentNum.get());
                }
            } else {
                // 数据库中存在阅读历史记录
                List<ArticleReadnum> list = articleReadnums.stream().filter(readNumItem -> item.equals(readNumItem.getArticleId())).collect(Collectors.toList());
                articleVo.setRedNum(list.get(0).getNum());
            }
            return articleVo;
        }).collect(Collectors.toList());
        result.setData(voList);
        return result;
    }

    @Override
    public Result updateArticle(Article article) {
        boolean success = updateById(article);
        if (success) {
            return Result.success();
        } else {
            return Result.fail();
        }
    }

    @Override
    public Result userReadHistory(String articleId, String userId) {
        // 绑定文章阅读量的key
        BoundHashOperations<String, String, Integer> articleBoundHashOps = redisTemplate.opsForHash().getOperations().boundHashOps(ArticleConstant.KEY_ARTICLE_READ_NUM + articleId);
        // 绑定用户阅读历史key
        BoundHashOperations<String, String, Integer> userBoundHashOps = redisTemplate.opsForHash().getOperations().boundHashOps(ArticleConstant.KEY_ARTICLE_USER_READ_HIS + userId);
        // 保存数据 如果保存成功那么今日该用户第一次阅读该文章
        Boolean isAdd = articleBoundHashOps.putIfAbsent(userId, 1);
        if (Boolean.TRUE.equals(isAdd)) {
            // 保存用户阅读历史
            Boolean isUserAdd = userBoundHashOps.putIfAbsent(articleId, 1);
            if (Boolean.TRUE.equals(isUserAdd)) {
                // 保存成功不需要做任何事
            } else {
                // 保存失败用户阅读量加1
                userBoundHashOps.increment(articleId, 1);
            }
        } else {
            // 文章该用户阅读量加一
            articleBoundHashOps.increment(userId, 1);
            // 用户阅读历史该阅读量加一
            userBoundHashOps.increment(articleId, 1);
        }
        // 最后返回成功
        return Result.success();
    }
}
